Skip to content

feat: secret version value redaction#5392

Merged
varonix0 merged 14 commits intomainfrom
daniel/secret-version-value-redaction
Feb 16, 2026
Merged

feat: secret version value redaction#5392
varonix0 merged 14 commits intomainfrom
daniel/secret-version-value-redaction

Conversation

@varonix0
Copy link
Member

@varonix0 varonix0 commented Feb 6, 2026

Context

Added support for redacting secret value versions. We update the actual secret value in-place of the secret version, and mark it as redacted. This is done to combat secret spill and avoid storing secret values elsewhere than just the latest secret version.

This PR also incldues a fix for the automatic updating of secret references not incrementing secret versions correctly.

Steps to verify the change

Type

  • Fix
  • Feature
  • Improvement
  • Breaking
  • Docs
  • Chore

Checklist

  • Title follows the conventional commit format: type(scope): short description (scope is optional, e.g., fix: prevent crash on sync or fix(api): handle null response).
  • Tested locally
  • Updated docs (if needed)
  • Read the contributing guide

@varonix0 varonix0 self-assigned this Feb 6, 2026
@maidul98
Copy link
Collaborator

maidul98 commented Feb 6, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 6, 2026

Greptile Overview

Greptile Summary

This PR implements secret version value redaction to combat secret spill by allowing users to redact (clear) secret values from historical versions while keeping metadata intact. The implementation also fixes a bug where automatic secret reference updates weren't incrementing versions correctly.

Key changes:

  • Added database columns isRedacted, redactedAt, redactedByUserId, and parentVersionId to track redaction state and version relationships
  • Implemented cascading redaction that automatically redacts child (replicated) secret versions when a parent is redacted
  • Fixed critical bug in secret reference update logic that failed to increment versions, causing duplicate version numbers and missing version history
  • Uses proper permission checks (ProjectPermissionSecretActions.Edit) and prevents redacting the latest version
  • Encrypts empty string for redacted values to prevent decryption errors
  • Frontend UI shows redacted versions with visual indicators and confirmation modal

Bug fix details:
The PR fixes an issue where updating secret references (when secrets are renamed or moved) would update the secret value but not properly create new versions. The fix uses $incr: { version: 1 } for atomic version increments and deduplicates updates via Map to prevent duplicate version creation when a secret references the same renamed secret multiple times.

Confidence Score: 4/5

  • Safe to merge with one potential edge case to address
  • The implementation is well-structured with proper permission checks, idempotent migrations, and comprehensive cascading logic. The bug fix for version increments is critical and properly implemented. Score reduced from 5 to 4 due to unbounded recursion risk in cascading redaction that could cause stack overflow with pathological data.
  • backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts needs attention for the unbounded recursion issue

Important Files Changed

Filename Overview
backend/src/db/migrations/20260205204147_redact-secret-version-values.ts Adds redaction columns (isRedacted, redactedAt, redactedByUserId) and parentVersionId to track version relationships - idempotent migration
backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts Implements redactSecretVersionValue with cascading redaction to child replicated versions, proper permission checks, and validation against redacting latest version
backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts Fixed version increment bug using $incr operator and deduplication via Map to prevent duplicate version creation when secrets reference renamed secrets multiple times
backend/src/services/secret-v2-bridge/secret-version-dal.ts Added findOne with joins to fetch project context, and findByParentVersionIds for cascading redaction queries
backend/src/ee/routes/v2/secret-version-router.ts New DELETE endpoint for redacting secret version values with audit logging
backend/src/ee/services/secret-replication/secret-replication-service.ts Tracks parentSecretVersionId when replicating secrets to establish parent-child version relationships for cascading redaction

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

25 files reviewed, 6 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 6, 2026

Additional Comments (4)

frontend/src/hooks/api/secrets/mutations.tsx
Unused mutation parameter

useRedactSecretValue declares variables { versionId: string; secretId: string }, but secretId is never used in the request and is only used to invalidate secretKeys.getSecretVersion(secretId). If the backend response already includes secretId, prefer using that for invalidation to avoid relying on caller-supplied IDs (and to keep the mutation signature minimal). Alternatively, remove secretId from the mutation variables and invalidate more broadly/appropriately.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!


backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts
Wrong foreign key stored

redactedByUserId is being set to actorId in redactSecretVersionValue, but actorId can be an identity or other actor type (you pass actor: req.permission.type). Since the migration defines redactedByUserId as a FK to Users.id, redacting as a non-user actor will violate the FK (or store incorrect data if FK enforcement differs).

Fix: only set redactedByUserId when actor is a user (or store a separate redactedByActorId + redactedByActorType).


backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts
Native regex used

destinationSecretPath.slice(1).replace(/\//g, ".") introduces a new native RegExp usage. Repo guidance requires using re2 for new regex to avoid ReDoS risks. Even though this pattern is simple, it still violates the policy.

Fix: replace this with a non-regex split/join (e.g. destinationSecretPath.slice(1).split("/").join(".")) or use re2.


backend/src/ee/routes/v2/secret-version-router.ts
Response schema mismatch

The route response is declared as SecretVersionsV2Schema.omit({ encryptedValue: true, encryptedComment: true }), but SecretVersionsV2Schema includes encryptedValue as a required field in the DB schema (and the service returns updatedSecretVersion from DAL). If encryptedValue is still present on the returned object, Fastify Zod response validation can fail (depending on how strict response validation is configured).

Fix: explicitly map/strip the returned secretVersion object to the response shape before returning, or adjust the schema to match the actual returned fields.

@varonix0
Copy link
Member Author

varonix0 commented Feb 11, 2026

@greptile, your additional comments:

backend/src/services/secret-v2-bridge/secret-v2-bridge-service.ts
Wrong foreign key stored
redactedByUserId is being set to actorId in redactSecretVersionValue, but actorId can be an identity or other actor type (you pass actor: req.permission.type). Since the migration defines redactedByUserId as a FK to Users.id, redacting as a non-user actor will violate the FK (or store incorrect data if FK enforcement differs).

Answer: Wrong. The route only accepts JWT auth (user auth)

frontend/src/hooks/api/secrets/mutations.tsx (Unused mutation parameter)

Answer: This is fine. In this case we don't use the response for invalidation

backend/src/services/secret-v2-bridge/secret-v2-bridge-fns.ts
Native regex used

Answer: Doesn't matter, it matches a single char. But I've refactored it to use .replaceAll instead

backend/src/ee/routes/v2/secret-version-router.ts
Response schema mismatch
The route response is declared as SecretVersionsV2Schema.omit({ encryptedValue: true, encryptedComment: true }), but SecretVersionsV2Schema includes encryptedValue as a required field in the DB schema (and the service returns updatedSecretVersion from DAL). If encryptedValue is still present on the returned object, Fastify Zod response validation can fail (depending on how strict response validation is configured).

Answer: Wrong. Even if the encryptedValue is included in the object, zod just strips it out so it's not returned in the request. This is the design we follow across the whole API.





With these comments and my latest changes in mind, re-review my PR and update your summary when you are done.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

27 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@varonix0 varonix0 merged commit 5651e61 into main Feb 16, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants